/**
 *
 * The MIT License
 *
 * Copyright (c) 2008 Board of Trustees, Leland Stanford Jr. University. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.macdadi.ks.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.Dictionary;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;
import org.macdadi.core.client.auth.AuthService;
import org.macdadi.core.client.auth.AuthenticatedUser;
import org.macdadi.core.client.auth.AuthenticationControl;
import org.macdadi.core.client.domain.DataService;
import org.macdadi.core.client.domain.ExtendedProjectInfo;
import org.macdadi.ks.client.View.ViewInfo;
import org.macdadi.ks.client.views.*;

/**
 * @author Ben Suter <ben.suter@gmail.com>
 */
public class SingleProject implements EntryPoint, HistoryListener, AuthenticationControl.AuthenticationListener, ViewContainer
{
    protected ExtendedProjectInfo currentProject;

    private static final View.Images images = (View.Images) GWT.create(View.Images.class);

    protected ViewList list = new ViewList(images);
    private ViewInfo curInfo;
    private View curView;
    private HTML description = new HTML();
    private DockPanel panel = new DockPanel();
    private VerticalPanel topPanel = new VerticalPanel();
    private VerticalPanel sideStrip = new VerticalPanel(); // TODO: eliminate panel, just use a DIV

    protected AuthenticationControl authControl = new AuthenticationControl();

    public AuthenticatedUser getCurrentUser() {
        return authControl.getAuthenticatedUser();
    }

    public void userAuthenticated(AuthenticatedUser user)
    {
        // do nothing for now
    }

    public void userLoggedOut()
    {
        // do nothing for now
    }

    public String getCurrentAuthToken() {
        if ( authControl.isAuthenticated() )
            return authControl.getAuthenticatedUser().getAuthToken();
        else
            return null;
    }

    public void checkOperationAuthorized(String target, String operation, AsyncCallback callback) {
        AuthService.App.getInstance().isAuthorizedOperation(getCurrentAuthToken(), getCurrentProject().getId(), target, operation, callback);
    }

    public AuthenticationControl getAuthenticationControl()
    {
        return authControl;
    }

    public ExtendedProjectInfo getCurrentProject() {
        return currentProject;
    }

    public void onHistoryChanged(String token)
    {
        // Find the ViewInfo associated with the history context. If one is
        // found, show it (It may not be found, for example, when the user mis-
        // types a URL, or on startup, when the first context will be "").
        ViewInfo info = list.find(token);
        if (info == null) {
            showWelcome();
            return;
        }
        show(info, false);
    }

    public void onModuleLoad()
    {
        authControl.setListener(this);
        
        // Load all the views.
        //loadViews();   // moved this to "initializeTheRest"
        
        panel.setVerticalAlignment(DockPanel.ALIGN_TOP);
        //panel.setHorizontalAlignment(DockPanel.ALIGN_LEFT);  // does this do anything?
        panel.setHeight("100%");

        // TODO: Remove the gap between tabs and description block
        authControl.addLink("Go to main project listing", "index.html", AuthenticationControl.SIDE_RIGHT);
        list.addHeaderItem(authControl);

        /* The topPanel displays the header with navigation and view info */
        topPanel.setVerticalAlignment(DockPanel.ALIGN_TOP);
        topPanel.setStyleName("macdadi-TopPanel");
        topPanel.add(list);
        topPanel.add(description);
        topPanel.setWidth("100%");
        panel.add(topPanel, DockPanel.NORTH);
        panel.setWidth("100%");

        /* The strip is a vertical bar along the left side, connecting with the
         * header. Together they frame each view.
         * Set the width of the strip using CSS .madadi-Sidestrip { width: 25px }
         * Same for height (I recommend 100%).
         */
        sideStrip.setStyleName("macdadi-Sidestrip");
        sideStrip.add(new HTML("&nbsp;"));
        panel.add(sideStrip, DockPanel.WEST);
        panel.setCellHeight(sideStrip, "100%");

        description.setStyleName("ks-Info");

        //History.addHistoryListener(this);  // moved this to "initializeTheRest"
        RootPanel.get().add(panel);

        /* Request current project info, wait until it has arrived, then continue with the rest */
        waitForProjectInitialized();
        // TODO: perhaps we should use DeferredCommand here?
    }

    public void initializeTheRest() {
        loadViews();

        History.addHistoryListener(this);

         // Show the initial screen.
        String initToken = History.getToken();
        if (initToken.length() > 0) {
            onHistoryChanged(initToken);
        } else {
            showWelcome();
        }       
    }

    public void show(ViewInfo info, boolean affectHistory)
    {
        // Don't bother re-displaying the existing views. This can be an issue
        // in practice, because when the history context is set, our
        // onHistoryChanged() handler will attempt to show the currently-visible
        // views.
        if (info == curInfo) {
            return;
        }
        curInfo = info;

        // Remove the old view from the display area.
        if (curView != null) {
            curView.onHide();
            panel.remove(curView);
        }

        // Get the new view instance, and display its description in the
        // view list.
        /* give the view a reference to its container (watch for cycles) */
        curView = info.getInstance(this);
        list.setViewSelection(info.getName());
        description.setHTML(info.getDescription());

        // If affectHistory is set, create a new item on the history stack. This
        // will ultimately result in onHistoryChanged() being called. It will call
        // show() again, but nothing will happen because it will request the exact
        // same view we're already showing.
        if (affectHistory) {
            History.newItem(info.getName());
        }

        // Change the description background color.
        DOM.setStyleAttribute(description.getElement(), "backgroundColor", info.getColor());
        DOM.setStyleAttribute(sideStrip.getElement(), "backgroundColor", info.getColor());

        // Display the new view.
        curView.setVisible(false);
        panel.add(curView, DockPanel.CENTER);
        panel.setCellWidth(curView, "100%"); /* Use as much as possible for the main content area */
        panel.setCellHorizontalAlignment(curView, VerticalPanel.ALIGN_CENTER);
        curView.setVisible(true);
        curView.onShow();
    }

    /**
     * Adds all views to the list. Note that this does not create actual instances
     * of all views yet (they are created on-demand). This can make a significant
     * difference in startup time.
     */
    protected void loadViews()
    {
        list.addView(Welcome.init());
        list.addView(Stakeholders.init());
        list.addView(Goals.init());
        list.addView(Preferences.init());
        list.addView(Options.init());
        list.addView(Impacts.init());
        list.addView(Values.init());
    }

    private void showWelcome()
    {
        show(list.find("Welcome"), false);
    }


    /**
     * Initialize the current project while displaying a "wait"
     * notice, then continue with the rest of the initialization.
     */
    public void waitForProjectInitialized() {
        if ( currentProject != null )
            return;

        final WaitIndicator wait = new WaitIndicator();
        wait.showCentered();

        /* Find out which project this is */
        Dictionary hostPageProject = Dictionary.getDictionary("project");
        final int projectId = Integer.parseInt(hostPageProject.get("id"));

        DataService.App.getInstance().getProject(projectId, new AsyncCallback()
        {
            public void onSuccess(Object result)
            {
                currentProject = (ExtendedProjectInfo) result;
                
                if (currentProject == null) {
                    // TODO: handle error
                    GWT.log("Unable to initialize currentProject from server, there is no project for id " + projectId, null);
                } else {
                    wait.hide();
                    initializeTheRest();
                }
            }

            public void onFailure(Throwable caught)
            {
                // TODO: handle error
                GWT.log("Unable to initialize currentProject from server.", caught);
            }
        });
    }

    private static class WaitIndicator extends PopupPanel
    {
        public WaitIndicator()
        {
            super(false); // do not autohide
            setWidget(new Label("Please wait while MACDADI starts ..."));
        }

        /**
         * Display this popup in the center of the window.
         */
        public void showCentered()
        {
            setPopupPositionAndShow(new PopupPanel.PositionCallback()
            {
                public void setPosition(int offsetWidth, int offsetHeight)
                {
                    int left = (Window.getClientWidth() - offsetWidth) / 3;
                    int top = (Window.getClientHeight() - offsetHeight) / 3;
                    setPopupPosition(left, top);
                }
            });
        }
    }
}
